package com.cskaoyan.gateway.controller.promotion;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.cskaoyan.gateway.cache.CacheManager;
import com.cskaoyan.gateway.form.promotion.CreatePromoOrderInfo;
import com.google.common.util.concurrent.RateLimiter;
import com.mall.commons.result.ResponseData;
import com.mall.commons.result.ResponseUtil;
import com.mall.promo.PromoService;

import com.mall.promo.dto.*;
import com.mall.user.annotation.Anoymous;
import com.mall.user.constants.SysRetCodeConstants;
import com.mall.user.intercepter.TokenIntercepter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.checkerframework.checker.units.qual.A;

import org.redisson.api.RateLimiterConfig;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.*;

@RestController
@RequestMapping("/shopping")
@Slf4j
public class PromoController {

    @Reference(check = false, timeout = 3000)
    PromoService promoService;

    @Autowired
    RedissonClient redissonClient;    //从redis中获取一把分布式锁

    @Autowired
    @Qualifier("cacheManager")
    CacheManager cacheManager;  //用于标记售罄状态---业务限流

    RateLimiter rateLimiter;   //实现令牌桶算法，进行限流

    ExecutorService executor;

    @PostConstruct
    public void init() {
        rateLimiter = RateLimiter.create(100);  //每秒钟，创建100个令牌
        //利用固定数量线程池，实现队列泄洪；保护“因为上游服务 积累请求  进而  导致下游服务 请求激增” 带来的危险
        executor = Executors.newFixedThreadPool(100);
    }


    @GetMapping("/seckilllist")
    @Anoymous
    public ResponseData getPromotionList(@RequestParam Integer sessionId) {
        PromoInfoRequest promoInfoRequest = new PromoInfoRequest();
        promoInfoRequest.setSessionId(sessionId);

        String yyyyMMdd = DateFormatUtils.format(new Date(), "yyyyMMdd");
        promoInfoRequest.setYyyymmdd(yyyyMMdd);
        PromoInfoResponse promoInfoResponse = promoService.getPromoList(promoInfoRequest);
        if (!promoInfoResponse.getCode().equals(SysRetCodeConstants.SUCCESS.getCode())) {
            return new ResponseUtil<>().setErrorMsg(promoInfoResponse.getMsg());
        }
        return new ResponseUtil<>().setData(promoInfoResponse);

    }


    @PostMapping("/promoProductDetail")
    public ResponseData getPromoItemDetail(@RequestBody PromoProductDetailRequest request) {

        PromoProductDetailResponse response = promoService.getPromoProductDetail(request);
        if (!response.getCode().equals(SysRetCodeConstants.SUCCESS.getCode())) {
            return new ResponseUtil<>().setErrorMsg(response.getMsg());
        }
        return new ResponseUtil<>().setData(response);
    }


    @PostMapping("/seckill")
    public ResponseData seckill(HttpServletRequest request,
                                @RequestBody CreatePromoOrderInfo createPromoOrderInfo) {

        //总体限流：使用令牌桶算法（谷歌提供工具类）
        rateLimiter.acquire();   //如果令牌桶暂时没有令牌，需要休眠一小段时间；获取到令牌，继续执行

        // 业务限流: 判断商品售罄标志
        Long productId = createPromoOrderInfo.getProductId();
        Long psId = createPromoOrderInfo.getPsId();

//        String userInfo = (String) request.getAttribute(TokenIntercepter.USER_INFO_KEY);
//        JSONObject jsonObject = JSON.parseObject(userInfo);
//        Integer uid = (Integer) jsonObject.get("uid");
//        String username = (String) jsonObject.get("username");
        Map userInfo = (Map) request.getAttribute(TokenIntercepter.USER_INFO_KEY);
        String username = (String) userInfo.get("username");
        Long uid = Long.parseLong((String) userInfo.get("uid"));

        /**
         * 业务限流：从业务逻辑上，进行限流；
         * 业务限流和总体限流，不一样；
         * 限量：判断商品是否已经售罄；如果售罄，下边就不需要继续了
         */
        //获取库存售罄的标志
        String promoStockKey = "promo_order_stock_not_enough" + "-" + productId + "-" + psId;
        String notEnough = cacheManager.checkCache(promoStockKey);

        if (notEnough != null && "none".equals(notEnough.trim())) {  //售罄
            //返回秒杀失败的响应
            return new ResponseUtil<>().setData("商品已经售罄");
        }


        /**
         * 一个用户，仅仅可以秒杀一次：根据userId和productId进行限制
         */
        Map<Long, Boolean> map = redissonClient.getMap(uid.toString());
        Boolean result = map.get(productId);
        if (result != null) {
            return new ResponseUtil<>().setErrorMsg("您已秒杀过！感谢下次秒杀");
        }

        CreatePromoOrderRequest createPromoOrderRequest = new CreatePromoOrderRequest();
        createPromoOrderRequest.setProductId(productId);
        createPromoOrderRequest.setPsId(psId);
        createPromoOrderRequest.setUserId(uid);
        createPromoOrderRequest.setUsername(username);

        //增加地址信息
        createPromoOrderRequest.setAddressId(createPromoOrderInfo.getAddressId());
        createPromoOrderRequest.setStreetName(createPromoOrderInfo.getStreetName());
        createPromoOrderRequest.setTel(createPromoOrderInfo.getTel());

//        CreatePromoOrderResponse createPromoOrderResponse = promoService.createPromoOrder(createPromoOrderRequest);
        /**
         * 将秒杀下单的任务，封装成Callable这样的异步任务，放在线程池中执行；
         * 通过 Future 获取返回值；一个future对象，代表一个异步任务执行的结果
         */
        Future<CreatePromoOrderResponse> future = executor.submit(new Callable<CreatePromoOrderResponse>() {
            @Override
            public CreatePromoOrderResponse call() throws Exception {
                return promoService.createPromoOrder(createPromoOrderRequest);
            }
        });
        CreatePromoOrderResponse createPromoOrderResponse = null;
        try {
            createPromoOrderResponse = future.get();    //从future代表一个对象：存储异步任务执行结果；所以，可以直接从future中取出异步任务执行的结果
        } catch (InterruptedException e) {
            e.printStackTrace();
            return new ResponseUtil<>().setData(createPromoOrderResponse);
        } catch (ExecutionException e) {
            e.printStackTrace();
            return new ResponseUtil<>().setData(createPromoOrderResponse);
        }

        log.info("promo controller submit.get return " + createPromoOrderResponse.getCode());
        if (!createPromoOrderResponse.getCode().equals(SysRetCodeConstants.SUCCESS.getCode())) {
            return new ResponseUtil<>().setErrorMsg(createPromoOrderResponse.getMsg());
        }else {
        return new ResponseUtil<>().setData(createPromoOrderResponse);
        }
    }
}

